<?php if(!defined('CALL')) exit('No direct script access allowed');

include_once('cfg/cfg.php');
include_once('lib/utils.class.php');
include_once('view/v_plans.class.php');
include_once 'models/db.class.php';
include_once('models/m_events.class.php');
include_once('models/m_messages.class.php');


class mUser
{
	// идентификатор пользователя в БД
	public $id = null;
	// свойства пользователя
	public $props = null;
	// статистика пользователя
	public $stat = null;
	// отношения с другими пользователями
	public $links = null;
	// события
	public $events = null;
	// список всех связанных пользователей 
	public $users = null;
	// сообщения
	public $messages = null;
	// новости
	public $news_num = 0;
	
	public function __construct($uid = null)
	{
		$this->links = array(
			'CM' => array(), // Candidates Master
			'FM' => array(), // Friends    Master
			'WM' => array(), // Watchers   Master
			'CS' => array(), // Candidates Slave
			'FS' => array(), // Friends    Slave
			'WS' => array()  // Watchers   Slave
			);
		
		$this->id = $uid;
		$this->events = array();
		$this->users = array();
	}
	
	private function init()
	{
		// проверяем попытку авторизации через POST
		if(isset($_POST[FORM_IDENTITY_FIELD]) && isset($_POST[FORM_PASSWORD_FIELD]))
		{
			// очистка следов предыдущих авторизаций
			$this->clear_track();
			
			// попытка авторизации
			$this->login($_POST[FORM_IDENTITY_FIELD], $_POST[FORM_PASSWORD_FIELD]);
		}
	}
	
	public function register($username, $email, $password, $born, $male, $city_id, $note)
	{
		$sql =
			"INSERT INTO users(
				city_id, username, password, mail, male, born, note)
			VALUES
				(%s, %s, %s, %s, %s, %s, %s)";
				
		$params = array(
			DB::get()->escape($city_id),
			DB::get()->escape(htmlspecialchars($username)),
			DB::get()->escape(md5(md5($password).SALT)),
			DB::get()->escape(htmlspecialchars($email)),
			DB::get()->escape($male),
			DB::get()->escape($born),
			DB::get()->escape(htmlspecialchars($note))
			);
		
		$sql = vsprintf($sql, $params);
		if(LOG_SQL) Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
		DB::get()->query($sql);
		
		$this->id = DB::get()->insert_id();
		$this->load();
		$_SESSION[UID] = $this->id;
	}
	
	public function update($username, $city_id, $male, $born, $note)
	{
		
		$sql =
			"UPDATE users SET".
				" username = %s,".
				" city_id = %s,".
				" male = %s,".
				" born = %s,".
				" note = %s".
			" WHERE id = ".$this->id;

		$params = array(
			DB::get()->escape($username),
			DB::get()->escape($city_id),
			DB::get()->escape($male),
			DB::get()->escape($born),
			DB::get()->escape($note)
			);
		
		$sql = vsprintf($sql, $params);
		if(LOG_SQL)
			Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
		DB::get()->query($sql);
	}
	
	public function update_avatar($avatar_name)
	{
		$sql = "UPDATE users SET face = '$avatar_name' WHERE id = ".$this->id;
		if(LOG_SQL) Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
		DB::get()->query($sql);
	}
	
	public function update_password($password)
	{
		$enc_pass = md5(md5($password).SALT);
		$sql = "UPDATE users SET `password` = '$enc_pass' WHERE id = ".$this->id;
		if(LOG_SQL) Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
		DB::get()->query($sql);
	}
	
	public function login($email, $password)
	{
		$this->id = null;
		
		$enc_password = md5(md5($password).SALT);
		
		$sql = "SELECT id, password FROM users WHERE mail = %s LIMIT 1";
		$params = array(DB::get()->escape($email));
		$sql = vsprintf($sql, $params);
		if(LOG_SQL) Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
		$res = DB::get()->query($sql);
		
		if(DB::get()->num_rows($res) > 0)
		{
			$row = DB::get()->fetch_assoc($res);
			$this->log('REG', '$row[password]: '.$row['password']);
			$this->log('REG', '$enc_password: '.$enc_password);
			if($row['password'] == $enc_password)
			{
				DB::get()->free_result($res);
				
				$this->id = $row['id'];
				$this->load();
				session_regenerate_id();				
				$this->update_user_session();
				$this->log('REG', 'Login');
				
				return TRUE;
			}
			mMessages::insert(0, $row['id'], 'ALERT', 'Была предпринята неудачная попытка входа на сайт с Вашим email. Вводился пароль "'.$password.'"');
		}
			
		DB::get()->free_result($res);
		$this->props = FALSE;
		$this->log('REG', "Login: $email/$password");
		return FALSE;
	}
	
	public function url_login($email, $hash)
	{
		$this->id = null;
		$this->props = FALSE;
		
		$sql = "SELECT mail FROM logins WHERE hash = %s";
		$params = array(DB::get()->escape($hash));
		$sql = vsprintf($sql, $params);
		if(LOG_SQL) Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
		$res = DB::get()->query($sql);
		
		// НЕУДАЧА: такого хеша не найдено
		if(DB::get()->num_rows($res) == 0) return FALSE;
		
		$row = DB::get()->fetch_assoc($res);
		
		// НЕУДАЧА: хэш найден, но у него другой email
		if($row['mail'] != $email) return FALSE;
		
		$this->id = self::get_id($email, null);

		// НЕУДАЧА: в таблице logins email есть, а в таб. users - нет
		if(is_null($this->id)) return FALSE;

		// УДАЧА, но запись в таблице следует удалить,
		// чтобы ей не воспользовались т.к. она - одноразовая
		$sql = "DELETE FROM logins WHERE hash = %s";
		$params = array(DB::get()->escape($hash));
		$sql = vsprintf($sql, $params);
		if(LOG_SQL) Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
		DB::get()->query($sql);
		
		// $this->load();
		// session_regenerate_id();
		// записываем user id в сессию
		$_SESSION[UID] = $this->id;
		
		// $this->update_user_session();
		
		header('Location: '.ROOT_URL, TRUE, 303);
		exit();
	}
	
	public function logout()
	{
		$this->log('DBG', 'logout');
		$this->clear_track();
		$this->id = null;
	}
	
	public function is_authorized()
	{
		return !is_null($this->id);
		// return isset($_SESSION[UID]) || isset($_COOKIE[SID]);
	}
	
	public function authorize()
	{
		$this->id = null;
		
		// проверка username & password переданных через POST
		if(isset($_POST['email']) && !empty($_POST['password']))
		{
			// Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, 'authorize by username/password');
			return $this->login($_POST['email'], $_POST['password']);
		}
		// вход по ссылке
		elseif(!empty($_GET['email']) && !empty($_GET['hash']) && ($_GET['categ'] == 'login'))
		{
			return $this->url_login($_GET['email'], $_GET['hash']);
		}
		// проверка в сессии user_id
		elseif(isset($_SESSION[UID]))
		{
			$this->id = $_SESSION[UID];
			// Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, 'authorize by session uid='.$_SESSION[UID]);
			$this->update_user_session();
			return $this->props !== FALSE;
		}
		// проверка куки с session_id
		elseif(isset($_COOKIE[SID]))
		{
			// Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, 'authorize by coockie sid='.$_COOKIE[SID]);
			$uid = $this->get_user_id_by_session($_COOKIE[SID]);
			if($uid)
			{
				$this->id = $uid;
				$this->log('DBG', 'user id='.$uid);
				$this->update_user_session();
				return $this->props !== FALSE;
			}
			else
			{
				return FALSE;
			}
		}
		// никаких зацепок нет, т.е. посетителя авторизовать невозможно
		else
		{
			$this->log('DBG', 'no authorization');
			return FALSE;
		}
	}
	
	public function load()
	{
		$this->props = self::get_props($this->id);
		$this->links = self::get_links($this->id);
		$this->news_num = mMessages::get_news_num($this->id);
		// $this->stat = self::get_stat($this->id);
	}
	
	public static function lost($email, $subj, $body_templ)
	{
		$id = self::get_id($email, null);
		// пользователь с таким email не найден
		if(is_null($id)) return FALSE;
		
		$hash = self::get_hash($email);
		$body = str_replace('{hash}', $hash, $body_templ);
		Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, 'LETTER: \n'.$body);
		if(Mailer::send(array($email), $subj, $body))
		{
			try
			{
				$sql = "INSERT INTO logins(`hash`, mail, ip) VALUES(%s, %s, %s)";
				$params = array(
					DB::get()->escape($hash),
					DB::get()->escape($email),
					DB::get()->escape($_SERVER['REMOTE_ADDR'])
					);
				$sql = vsprintf($sql, $params);
				if(LOG_SQL) Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
				DB::get()->query($sql);
				return TRUE;
			}
			catch(Exception $e)
			{
				Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, 'ERROR: '.$e->getMessage());
				return FALSE;
			}
		}
		else
		{
			Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, 'ERROR: Mailer return false');
			return FALSE;
		}
	}
	
	public static function get_id($email, $def_id)
	{
		if(empty($email)) return $def_id;
		
		$sql = "SELECT id FROM users WHERE mail = '".$email."'";
		if(LOG_SQL) Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
			
		$res = DB::get()->query($sql);
		if(DB::get()->num_rows($res) > 0)
		{
			$row = DB::get()->fetch_row($res);
			return $row[0];
		}
		else
			return $def_id;
	}
	
	public static function get_hash($email)
	{
		return md5(date('His').$email.SALT);
	}
	
	public static function get_props($uid, $array_result = FALSE)
	{
		if(is_null($uid)) return FALSE;
		if(strlen($uid) == 0) return FALSE;
		
		$sql =
			"SELECT u.id, u.city_id, u.username, u.password, u.mail, ".
				"u.male, u.born, u.face, u.note, c.name as city ".
			  "FROM users u, cities c ".
			 "WHERE u.id in ($uid) ".
			   "AND c.id = u.city_id";
				
		if(LOG_SQL) Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
			
		$res = DB::get()->query($sql);

		$num_row = DB::get()->num_rows($res);
		if($num_row > 0 && $array_result)
		{
			$props = array();
			while($row = DB::get()->fetch_assoc($res))
			{
				$row['note'] = Utils::safe_encode($row['note']);
				$props[$row["id"]] = $row;
			}
			DB::get()->free_result($res);
			return $props;
		}
		elseif($num_row > 0)
		{
			$row = DB::get()->fetch_assoc($res);
			$row['note'] = Utils::safe_encode($row['note']);
			return $row;
		}
		else
			return FALSE;
	}
	
	public static function get_stat($uid)
	{
		$stat = array();
		
		if(is_null($uid)) return $stat;
		
		$sql = "
			SELECT 'ALL', COUNT(id) FROM `events`
			 WHERE user_id = $uid
			 UNION ALL
			SELECT 'FUT', COUNT(id) FROM `events`
			 WHERE user_id = $uid
			   AND dt_beg >= CURDATE()
			 UNION ALL
			SELECT 'SHA', COUNT(id) FROM event_users eu
			 WHERE user_id = $uid AND categ = 'SHA'
			 UNION ALL
			SELECT 'POP', COUNT(DISTINCT eu.id) FROM event_users eu, `events` e
			 WHERE e.user_id = $uid AND eu.event_id = e.id AND eu.categ = 'SHA'";
				
		if(LOG_SQL)
			Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
			
		$res = DB::get()->query($sql);
		while($row = DB::get()->fetch_row($res))
			$stat[$row[0]] = $row[1];
		DB::get()->free_result($res);
		
		return $stat;
	}
	
	public static function get_links($uid)
	{
		$links = array(
			'CM' => array(), // Candidates Master (кто запросил дружбу у актора)
			'FM' => array(), // Friends    Master (друзья актора)
			'WM' => array(), // Watchers   Master (за кем следит актор)
			'CS' => array(), // Candidates Slave  (кому актор предложил дружбу)
			'FS' => array(), // Friends    Slave  (у кого актор в друзьях)
			'WS' => array()  // Watchers   Slave  (кто следит за актором)
			);
		
		if(is_null($uid)) return $links;
		
		$sql =
			"SELECT uid1, uid2, link_type FROM user_links ".
			 "WHERE uid1 = $uid OR uid2 = $uid";
			
		if(LOG_SQL)
			Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
			
		$res = DB::get()->query($sql);
		if(DB::get()->num_rows($res) > 0)
		{
			while($row = DB::get()->fetch_assoc($res))
			{
				// Если это связь, где пользователь - субъект (актор)
				if($row['uid1'] == $uid)
				{
					switch ($row['link_type'])
					{
						// запрос на дружбу к другому пользователю
						case 'C':
							$links['CS'][] = $row['uid2'];
							break;
						// друг пользователя
						case 'F':
							$links['FM'][] = $row['uid2'];
							break;
						// за кем следит пользователь
						case 'W':
							$links['WM'][] = $row['uid2'];
							break;
					}
				}
				// это связь, где пользователь - объект
				else
				{
					switch ($row['link_type'])
					{
						// запрос на дружбу от других пользователей
						case 'C':
							$links['CM'][] = $row['uid1'];
							break;
						// пользователь в другзьях у ...
						case 'F':
							$links['FS'][] = $row['uid1'];
							break;
						// кто следит за пользователем
						case 'W':
							$links['WS'][] = $row['uid1'];
							break;
					}
				}
			}
			DB::get()->free_result($res);
		}
		
		return $links;
	}
	
	public function add_link($uid, $link_type)
	{
		// массив связей в php,
		// вторая буква - "М", т.к. связи устанавливает actor
		$key = $link_type.'M';
		// может ли существовать такой тип связи
		if(array_key_exists($key, $this->links))
		{
			// такой связи еще не существует существует
			if(array_search($uid, $this->links[$key]) === FALSE)
			{
				$sql = "INSERT INTO user_links(uid1, uid2, link_type) ".
					"VALUES(".$this->id.", $uid, '$link_type')";

				if(LOG_SQL) Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
				DB::get()->query($sql);
				$id = DB::get()->insert_id();
				
				if(DB::get()->affected_rows($sql) > 0)
				{
					mMessages::forLink($uid, $this, $link_type, 'ins');
					/*
					if(empty($this->props)) $this->load();
					
					$msg = 'ERROR';
					$categ = 'SYS';
					switch($link_type)
					{
						case 'F':
							$categ = 'FRIEND';
							$msg = $this->props['username'].' начал доверять Вам'; 
							break;
						
						case 'W':
							$categ = 'WATCH';
							$msg = $this->props['username'].' стал следить за Вами'; 
							break;
					}
					mMessages::insert($this->id, $uid, $categ, $msg);
					*/
				}
				$this->links[$key][] = $uid;
				
				return $id;
			}
		}
		return FALSE;
	}
	
	public function delete_link($uid, $link_type)
	{
		$sql = "DELETE FROM user_links WHERE uid1 = ".$this->id." AND uid2 = $uid AND link_type = '$link_type';";
		if(LOG_SQL) Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
		DB::get()->query($sql);

		if(DB::get()->affected_rows($sql) > 0)
		{
			mMessages::forLink($uid, $this, $link_type, 'del');
			/*
			if(empty($this->props)) $this->load();
			$msg = 'ERROR';
			$categ = 'SYS';
			switch($link_type)
			{
				case 'F':
					$categ = 'FRIEND';
					$msg = $this->props['username'].' перестал доверять Вам'; 
					break;
				
				case 'W':
					$categ = 'WATCH';
					$msg = $this->props['username'].' больше не следит за Вами'; 
					break;
			}
			mMessages::insert($this->id, $uid, $categ, $msg);
			*/
		}

		// массив связей в php,
		// вторая буква - "М", т.к. связи устанавливает actor
		$key = $link_type.'M';
		// может ли существовать такой тип связи
		if(array_key_exists($key, $this->links))
		{
			// индекс связи 
			$i = array_search($uid, $this->links[$key]);
			if($i !== FALSE)
				array_splice($this->links[$key], $i, 1);
		}
	}
	
	public function load_users()
	{
		$this->users = array();
		// надо получить единый массив ID пользователей,
		// по которому сначала будет SQL запрос,
		// а потом - создан массив свойств
		$list = array_merge(
			$this->links['CM'],
			$this->links['FM'],
			$this->links['WM'],
			$this->links['CS'],
			$this->links['FS'],
			$this->links['WS']
			);
		
		$list = array_unique($list);
		
		// получаем перечень ID через запятую
		$ids = implode(",", $list);
		
		// получаем массив свойств для всех пользователей
		$this->users = self::get_props($ids, TRUE);
	}

	public function load_events($date_uni, $city_id, $author_id, $share, $sub, $own)
	{
		$row_beg = 0;
		$row_num = NUM_ROWS;
		$dir = TRUE;
		
		if(!$this->is_authorized())
		{
			$author_id = null;
			$share = FALSE;
			$pub = TRUE;
			$sub = FALSE;
			$pri = FALSE;
		}
		$sql = mEvents::generate_sql($this->id, $author_id, $city_id, $date_uni, $share, $sub, $own);
		// $sql = Events::select($date_uni, $row_beg, $row_num, $dir, $city_id, $this->id, $author_id, $share, $pub, $sub, $pri);
		// сохраняем полученный SELECT
		$_SESSION['EVENTSQL'] = $sql;
		$this->events = mEvents::read($sql, 0, NUM_ROWS, TRUE, $this->id);
		return $this->events;
	}
	
	public function more_events($row_beg, $row_num, $dir)
	{
		$events = mEvents::read($_SESSION['EVENTSQL'], $row_beg, $row_num, $dir, $this->id);
		
		if($dir)
			$this->events = array_merge($this->events, $events);
		else
			$this->events = array_merge($events, $this->events);
		
		return $events;
	}

	public function load_messages($user_id = null)
	{
		if(($user_id == null) || ($user_id == $this->id))
		{
			$this->messages = mMessages::select_system($this->id);
		}
		else
		{
			$this->messages = mMessages::select_private($this->id, $user_id);
		}
	}
	
	public function get_user_id_by_session($sid)
	{
		$sql = "SELECT user_id FROM sessions WHERE session_id = '$sid'";
		
		if(LOG_SQL)
			Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
			
		$res = DB::get()->query($sql);
		if(DB::get()->num_rows($res) > 0)
		{
			$row = DB::get()->fetch_assoc($res);
			return $row['user_id'];
		}
		else
			return FALSE;
	}
	
	private function clear_track()
	{
		session_destroy();
		session_start();
		// unset($_SESSION[UID]);
		unset($_COOKIE[SID]);
		setcookie(SID, '0', time() - 86400, '/');
		setcookie(CMD, '0', time() - 86400);
	}
	
	private function update_user_session($data = null)
	{
		if(is_null($this->id)) return FALSE;
		
		// записываем user id в сессию
		$_SESSION[UID] = $this->id;

		// установка (обновление) в куке на 30 дней id текущей сессии
		$sid = session_id();
		setcookie(SID, $sid, time() + 2592000, '/');
		
		// проверка существования сессии
		$uid = $this->get_user_id_by_session($sid);
		// Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, '$uid: '.$uid);
		// сессия в БД есть!
		if($uid)
		{
			$sql = "UPDATE sessions SET ".
				"ip_address = %s, ".
				"user_agent = %s, ".
				"user_data = %s, ".
				"last_activity = NOW() ".
				"WHERE session_id = %s";
			
			$params = array(
				DB::get()->escape($_SERVER['REMOTE_ADDR']),
				DB::get()->escape($_SERVER['HTTP_USER_AGENT']),
				DB::get()->escape($data),
				DB::get()->escape($sid)
				);
		}
		else
		{
			$sql =
				"INSERT INTO sessions(session_id, user_id, ip_address, user_agent, user_data) ".
				"VALUES(%s, %s, %s, %s, %s)";
			$params = array(
				DB::get()->escape($sid),
				DB::get()->escape($this->id),
				DB::get()->escape($_SERVER['REMOTE_ADDR']),
				DB::get()->escape($_SERVER['HTTP_USER_AGENT']),
				DB::get()->escape($data)
				);
		}
		$sql = vsprintf($sql, $params);		
		if(LOG_SQL)	Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
		DB::get()->query($sql);
	}
	
	private function log($categ, $info)
	{
		if($categ == 'DBG' && DEBUG_MODE === FALSE) return;
		
		$uid = ($this->props === FALSE) ? null : $this->props['id'];
		$ip = $_SERVER['REMOTE_ADDR'];
		$ua = $_SERVER['HTTP_USER_AGENT'];
		$ref = $_SERVER['HTTP_REFERER'];
		
		Logger::Write('user.class', "$categ\t$info");
		
		$sql =
			"INSERT INTO user_log(user_id, ip_address, useragent, categ, referer, info) ".
			"VALUES(%s, %s, %s, %s, %s, %s)";
			
		$params = array(
			DB::get()->escape($uid),
			DB::get()->escape($ip),
			DB::get()->escape($ua),
			DB::get()->escape($categ),
			DB::get()->escape($ref),
			DB::get()->escape($info)
			);
		$sql = vsprintf($sql, $params);
		if(LOG_SQL) Logger::Write(__FILE__." ".__FUNCTION__." line: ".__LINE__, $sql);
			
		DB::get()->query($sql);
	}
	
}